/*
MAXCLIENT-ben definiált maximális számú klienssel összeköttetés létesítése
A klienseket láncolt listában tárolja
Kilépő klienseket helyesen lekezeli, a láncolt listából törli
Ha bármely klienstől “START” parancs érkezik, a szerver “KEZDODIK” üzenetet küld az összes kliensének.
Ha valaki a játék kezdete után akar csatlakozni, a szerver nem engedi, s „A játék már elkezdődött“ üzenettel elküldi.
A játék során a kapcsolódó kliensek egymás után kapnak lehetőséget a következő beszélgetésre:
A szerver megkérdezi, sorsoljon-e ki még a kliens számára számot.
Ha a válasz „I“, akkor a szerver generál egy véletlen számot 2 és 11 között,
s hozzáadja az adott kliens eredetileg 0-ról induló számlálójához, s ismételten felteszi a kérdést.
Ha a válasz „N“, akkor ha van további játékos, akkor vele végzi el ugyanezt, ellenkező esetben eredményt hirdet.
Eredményhirdetés során a következő az eredmény:
Ha minden kliens számlálója 21 feletti, akkor nincs nyertes az adott körben.
A nyertes az, akinek számlálója alulról közelítve a legközelebb van 21-hez. (Holtverseny is lehet).
Az eredményről minden klienst értesíteni kell. Az eredményhirdetés után a szerver felteszi a kérdést minden kliensnek,
hogy szerene-e újra játszani. „I“ választ adó kliensek maradnak új játékra, „N“ választ adókat kilépteti a szerver.
Ekkor újabb „START“ parancsig megint lehetőségük van új klienseknek csatlakozni.

+1 jegy:
A játék folyamán “?” parancs hatására küldje el a szerver a kérdezőnek a számlálók aktuális állását!

+ 1 jegy:
A szerver az eredményhirdetéskor küldje ki azt is, milyen IP-vel és port-tal rendelkezik a nyertes kliens.

+ 1 jegy:
Az alap játék módosuljon úgy, hogy ha egy játék már kezdetét vette, az újonnan csatlakozó klienseket ne küldje el
a szerver, hanem fork-kal kezdjen nekik új játékot.
*/

#include <stdio.h>
#include <stdlib.h>

#include <string.h>

#ifdef _WIN32
	#include <winsock2.h>
	#include <ws2tcpip.h>
	#pragma comment(lib, "ws2_32.lib")
#else
	#include <netdb.h>
#endif


#define MAXC 2
#define port "2222"

struct kliens{
	int ID;
	int fd;
	struct kliens* next;
	int sum;
	int helyezes;
};
struct kliens* kliensek=NULL;
int kliensszam=0;

int listenSock;
fd_set master;
int fdmax;

int jatek=0;
struct kliens* aktualisjatekos;
int helyezes;

void felvesz(int ujfd){
	char uz[200];
	struct kliens* kl=(struct kliens*)malloc(sizeof(struct kliens));
	kl->fd=ujfd;
	kl->next=kliensek;
	kl->sum=0;
	kl->helyezes=0;
	kliensek=kl;
	FD_SET(ujfd,&master);
	if (ujfd>fdmax) {
		fdmax=ujfd;
	}
	kliensszam++;
	kl->ID=kliensszam;

	memset(uz,'\0',200);
	strcat(uz,"\E[2J \E[0;0f \E[0;4m csatlakozva \E[0m \n");
	strcat(uz,"");
	send(ujfd,uz,sizeof uz,0);
}

void elutasit(int ujfd){
	char uz[200];
	memset(uz,'\0',200);
	strcat(uz,"\E[2J \E[0;0f\n\n");
	send(ujfd,uz,sizeof uz,0);
	if(jatek){
		memset(uz,'\0',200);
		strcat(uz,"jatek elkezdodott\n");
	}
	else{
		memset(uz,'\0',200);
		strcat(uz,"maxclient\n");
	}

	send(ujfd,uz,sizeof uz,0);
	#ifdef _WIN32
		closesocket(ujfd);
	#else
		close(ujfd);
	#endif

}

void torol(int i){
	int j;
	int ujfdmax=0;
	struct kliens* kl=kliensek;
	if (kl->fd==i) {
		kl=kliensek;
		kliensek=kl->next;
	} else {
		struct kliens* elozo=kliensek;
		kl=kliensek->next;
		while(kl->fd!=i){
			elozo=kl;
			kl=kl->next;
		}
		elozo->next=kl->next;
	}

	FD_CLR(i,&master);


	for (j = 0; j <= fdmax; ++j) {
		if(FD_ISSET(j,&master)){
			if (j>ujfdmax) {
				ujfdmax=j;
			}
		}
	}
	fdmax=ujfdmax;
	
	#ifdef _WIN32
		closesocket(kl->fd);
	#else
		close(kl->fd);
	#endif
	
	printf("torolve: %d\n",kl->ID);
	fflush(stdout);
	free(kl);
	kliensszam--;

}

void sendallclient(char s[200]){
	struct kliens* kl=kliensek;
	while(kl!=NULL){
		send(kl->fd,s,strlen(s),0);
		kl=kl->next;
	}
}

int sorsol(int i){
	int ret=-1;
	srand ( time(NULL) );

	while(!(ret<=11 && ret>=2)){
		ret=rand() % 12;
	}
	return ret;
}

void helyezesszamol(){
	struct kliens* kl;
	int min=99;

	kl=kliensek;
	while(kl!=NULL){
		if(21-kl->sum<min && kl->helyezes==0){
			min=21-kl->sum;
		}
		kl=kl->next;
	}

	kl=kliensek;
	while(kl!=NULL){
		if(21-kl->sum==min){
			kl->helyezes=helyezes;
		}
		kl=kl->next;
	}
	if (min>=0)
		helyezes++;
}

void eredmenyhirdetes(){
	struct kliens* kl=kliensek;
	char uz[200];

	helyezes=1;

	jatek=0;
	sendallclient("eredmenyhirdetes\n");

	kl=kliensek;
	while(kl!=NULL){
		helyezesszamol();
		kl=kl->next;
	}
	
	kl=kliensek;
	while(kl!=NULL){
		memset(uz,'\0',200);
		sprintf(uz,"eredmeny: %d\n",kl->helyezes);
		if(kl->sum>21){
			strcpy(uz,"nem nyert!\n");
		}
		send(kl->fd,uz,sizeof uz,0);
		kl=kl->next;
	}
	
	sendallclient("uj jatek [i/n]\n");
}
void jatekuzenet(){
	char uz[200];
	memset(uz,'\0',200);
	strcat(uz,"i: sorsolas, n vege\n");
	send(aktualisjatekos->fd,uz,sizeof uz,0);
}

void pontnullaz(){
	struct kliens* kl=kliensek;
	while(kl!=NULL){
		kl->sum=0;
		kl->helyezes=0;
		kl=kl->next;
	}
}

void uzenetfeldolgoz(int i){
	char buff[200];
	int ret=-1;
	char uz[200];
	int fogadottbyteok=0;
	struct kliens* kl=kliensek;
	while(kl->fd!=i){
		kl=kl->next;
	}

	memset(buff,'\0',200);
	fogadottbyteok=recv(i,buff,sizeof buff,0);
	if (fogadottbyteok<=0) {
		torol(i);
	} else {
		if(strncmp(buff,"t",1)==0){

			while(!(ret<=21 && ret>=2)){
				ret=rand() % 22;
			}

			memset(uz,'\0',200);
			sprintf(uz,"%d\n",ret);
			send(i,uz,sizeof uz,0);
			return;
		}
		if(strncmp(buff,"q",1)==0){
			torol(i);
			return;
		}
		if(strncmp(buff,"?",1)==0){
			memset(uz,'\0',200);
			sprintf(uz,"summa: %d\n",kl->sum);
			send(i,uz,sizeof uz,0);
			return;
		}
		if (!jatek && (strncmp(buff,"s",1)==0)){
			jatek=1;
			pontnullaz();
			sendallclient("kezdodik\n");
			aktualisjatekos=kliensek;
			jatekuzenet();
			return;
		}

		if (jatek && (strncmp(buff,"i",1)==0) && aktualisjatekos->ID==kl->ID){
			kl->sum+=sorsol(i);

			memset(uz,'\0',200);
			sprintf(uz,"%d\n",kl->sum);
			send(i,uz,sizeof uz,0);
			jatekuzenet();
			return;
		}

		if (!jatek && (strncmp(buff,"i",1)==0)){
			pontnullaz();
			aktualisjatekos=kliensek;
			return;
		}
		if (!jatek && (strncmp(buff,"n",1)==0)){
			torol(i);
			return;
		}

		if (jatek && (strncmp(buff,"n",1)==0) && aktualisjatekos->ID==kl->ID){
			aktualisjatekos=aktualisjatekos->next;
			if (aktualisjatekos==NULL) {
				eredmenyhirdetes();
				return;
			}
			jatekuzenet();
			return;
		}
		if (jatek){

			memset(uz,'\0',200);
			strcat(uz,"nem te jossz\n");
			send(i,uz,sizeof uz,0);
		}

	}
}

void selectloop(){
	int i;
	fd_set readfd;
	FD_ZERO(&readfd);
	while(1){
		readfd=master;
		select(fdmax+1,&readfd,NULL,NULL,NULL);
		for (i = 0; i <= fdmax; ++i) {
			if (FD_ISSET(i,&readfd)) {
				if (i==listenSock) {
					struct sockaddr sa;
					socklen_t sl=sizeof sa;
					int ujfd=accept(listenSock,&sa,&sl);
					if (kliensszam<MAXC) {
						if (!jatek) {
							felvesz(ujfd);
						} else {
							elutasit(ujfd);
						}

					} else {
						elutasit(ujfd);
					}
				} else {
					uzenetfeldolgoz(i);
				}
			}
		}
	}
}

void serverlisten(){
	int v=1;
	struct addrinfo krit;
	struct addrinfo* res=NULL;
	memset(&krit,0,sizeof(krit));
	krit.ai_family=AF_UNSPEC;
	krit.ai_socktype=SOCK_STREAM;
	krit.ai_flags=AI_PASSIVE;
	krit.ai_protocol = IPPROTO_TCP;
	getaddrinfo(NULL,port,&krit,&res);
	listenSock=socket(res->ai_family,res->ai_socktype,res->ai_protocol);

	setsockopt(listenSock,SOL_SOCKET,SO_REUSEADDR,&v,sizeof v);
	if(bind(listenSock,res->ai_addr,res->ai_addrlen)){
		printf("bind error\n");
		fflush(stdout);
	}
	listen(listenSock,MAXC);
	FD_ZERO(&master);
	FD_SET(listenSock,&master);
	fdmax=listenSock;
}

int main(int argc, char **argv) {
	srand ( time(NULL) );
	#ifdef _WIN32
		WORD wVersionRequested;
		WSADATA wsaData;
		int err;

		wVersionRequested = MAKEWORD(2, 2);

		err = WSAStartup(wVersionRequested, &wsaData);
		if (err != 0) {                                 
			printf("WSAStartup failed with error: %d\n", err);
			return 1;
		}
	#endif

	serverlisten();
	selectloop();

	#ifdef _WIN32
		WSACleanup();
	#endif
	return 0;

}

